#include <string.h>
#include "esp_wifi.h"

//#include "esp_system.h"
#include "esp_log.h"
//#include "esp_timer.h"
//#include "esp_event_loop.h"
//#include "nvs_flash.h"

//#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
//#include "freertos/event_groups.h"

//#include "esp_libc.h"
#include "freertos/timers.h"

#include "./inc/sniffer.h"

typedef struct {
    uint8_t mac[6];
    int8_t channel;           /**< which channel this packet in */
    signed rssi: 8;           /**< signal intensity of packet */
} sniffer_last_info_t;

static const char *TAG = "sniffer";
#define READ_BUF_MAX    100


static TimerHandle_t timer_hop = NULL;
SemaphoreHandle_t semaphore_resource = NULL;
//共享资源
static sniffer_cb_t read_cb = NULL;
sniffer_mac_info_t *pread_buf = NULL;
uint16_t read_buf_index = 0;
sniffer_last_info_t last_buf = {0};
static char printbuf[30];


static void sniffer_cb(void* buf, wifi_promiscuous_pkt_type_t type);
static void sniffer_channel_switch(TimerHandle_t pxTimer);
static void sniffer_read_task(void* pvParameters);

void sniffer_init(void)
{
    wifi_promiscuous_filter_t sniffer_filter = {0};

    if(timer_hop != NULL)
    {
        ESP_LOGD(TAG, "Please do't reinitialize!");
        return;
    }
    
#if CONFIG_FILTER_MASK_MGMT
    sniffer_filter.filter_mask |= WIFI_PROMIS_FILTER_MASK_MGMT;
#endif

#if CONFIG_FILTER_MASK_CTRL
    sniffer_filter.filter_mask |= WIFI_PROMIS_FILTER_MASK_CTRL;
#endif

#if CONFIG_FILTER_MASK_DATA
    sniffer_filter.filter_mask |= WIFI_PROMIS_FILTER_MASK_DATA;
#endif

#if CONFIG_FILTER_MASK_MISC
    sniffer_filter.filter_mask |= WIFI_PROMIS_FILTER_MASK_MISC;
#endif
    
    if(sniffer_filter.filter_mask == 0)
    {
        ESP_LOGW(TAG, "Please add one filter at least!");
        return;
    }

    timer_hop = xTimerCreate("channel switch",250 / portTICK_PERIOD_MS,pdTRUE,NULL,&sniffer_channel_switch);
    //timer_hop = xTimerCreate("channel switch",1000 / portTICK_PERIOD_MS,pdTRUE,NULL,&sniffer_channel_switch);

    if(timer_hop == NULL)
    {
        ESP_LOGE(TAG, "Can't creat timer!");
        return;
    }
    if(semaphore_resource == NULL)
    {
        semaphore_resource = xSemaphoreCreateMutex();
    }
    ESP_ERROR_CHECK(esp_wifi_set_promiscuous_rx_cb(sniffer_cb));
    ESP_ERROR_CHECK(esp_wifi_set_promiscuous_filter(&sniffer_filter));
}

void sniffer_start(void)
{
    ESP_LOGV(TAG, "  - sniffer start.");
    if( xTimerStart( timer_hop, 0 ) != pdPASS )
    {
        ESP_LOGE(TAG, "The timer could not start!");
        return;
    }
    ESP_ERROR_CHECK(esp_wifi_set_channel(1, 0));
    //ESP_ERROR_CHECK(esp_wifi_set_channel(1, 0));
    
    ESP_ERROR_CHECK(esp_wifi_set_promiscuous(true));
    return;
}

void sniffer_stop(void)
{
    if( xTimerStop( timer_hop, 0 ) != pdPASS )
    {
        ESP_LOGE(TAG, "The timer could not stop!");
        return;
    }
    ESP_ERROR_CHECK(esp_wifi_set_promiscuous(false));
}

void sniffer_set_read_cb(sniffer_cb_t pcallback)
{
    if(xSemaphoreTake(semaphore_resource,portMAX_DELAY) == pdTRUE)
    {
        if(pcallback == NULL && pread_buf != NULL)
        {
            free(pread_buf);
            pread_buf = NULL;
        }
        else if(pcallback != NULL && pread_buf == NULL)
        {
            pread_buf = malloc(READ_BUF_MAX * sizeof(sniffer_mac_info_t));
            if(pread_buf == NULL)
            {
                ESP_LOGE(TAG, "  - malloc fail!");
                return;
            }
            read_buf_index = 0;
        }
        read_cb = pcallback;
        xSemaphoreGive(semaphore_resource);
    }
}

static void sniffer_cb(void* buf, wifi_promiscuous_pkt_type_t type)
{
    wifi_pkt_rx_ctrl_t* rx_ctrl = (wifi_pkt_rx_ctrl_t*)buf;
    uint8_t* frame = (uint8_t*)(rx_ctrl + 1);
    //uint32_t len = rx_ctrl->sig_mode ? rx_ctrl->HT_length : rx_ctrl->legacy_length;
    uint32_t i;
    uint8_t* src_mac = frame + 10;
    uint8_t frame_control_buf = *(frame + 1) & 0x03;

    //不处理ap发送的数据
    /*
    if(frame_control_buf != 0x01)
    {
        if(frame_control_buf == 0x00)
        {
            if(memcmp(frame + 10, frame + 16, 6) == 0)
            {
                return;
            }
        }
        else
        {
            return;
        }
    }*/
    if(frame_control_buf != 0x01 && (frame_control_buf != 0x00 || memcmp(src_mac, frame + 16, 6) == 0))
    {
        free(buf);
        return;
    }

    //过滤伪MAC
#if CONFIG_FILTER_FALSE_MAC
    if(*(src_mac) & 0x03)
    {
        free(buf);
        return;
    }
#endif

    if(xSemaphoreTake(semaphore_resource,1) == pdTRUE)
    {
        //printf("%02x:%02x:%02x\n",*(src_mac+3),*(src_mac+4),*(src_mac+5));
        if(last_buf.channel > 0)
        {
            if(memcmp(src_mac, last_buf.mac, 6) == 0)
            {
                if(rx_ctrl->rssi > last_buf.rssi)
                {
                    last_buf.rssi = rx_ctrl->rssi;
                }
            }
            else
            {
                if(read_cb == NULL)
                {
                    for(i = 0; i < 6; i++)
                    {
                        sprintf(printbuf + i * 3, "%02x:", last_buf.mac[i]);
                    }
                    sprintf(printbuf + 17, " %d %d", last_buf.rssi,last_buf.channel);
                    printf("%s\n",printbuf);
                }
                else
                {
                    for(i = 0; i < read_buf_index; ++i)
                    {
                        if(memcmp(last_buf.mac ,(pread_buf + i)->mac ,6) == 0 )
                        {
                            if(last_buf.rssi > (pread_buf + i)->rssi)
                            {
                                (pread_buf + i)->rssi = last_buf.rssi;
                            }
                            break;
                        }
                    }
                    if( i == read_buf_index)
                    {
                        memcpy((pread_buf + i)->mac,last_buf.mac,6);
                        (pread_buf + i)->rssi = last_buf.rssi;
                        ++read_buf_index;
                        if(read_buf_index >= READ_BUF_MAX)
                        {
                            read_buf_index = READ_BUF_MAX - 1;
                        }
                    }
                }

                memcpy(last_buf.mac,src_mac,6);
                last_buf.rssi = rx_ctrl->rssi;
            }
        }
        else
        {
            memcpy(last_buf.mac,src_mac,6);
            last_buf.rssi = rx_ctrl->rssi;
            last_buf.channel = rx_ctrl->channel;
        }
        xSemaphoreGive(semaphore_resource);

    }

    free(buf);
}

static void sniffer_channel_switch(TimerHandle_t pxTimer)
{
    // 1 - 13 channel hopping
    uint8_t new_channel;
    uint32_t i;

    ESP_ERROR_CHECK(esp_wifi_get_channel(&new_channel,NULL));
    new_channel =  new_channel % 13 + 1;
    ESP_LOGV(TAG, "  - channel switch: %d", new_channel);
    ESP_ERROR_CHECK(esp_wifi_set_channel(new_channel,0));

    if(xSemaphoreTake(semaphore_resource,portMAX_DELAY) == pdTRUE)
    {
        if(last_buf.channel > 0)
        {
            if(read_cb == NULL)
            {
                for(i = 0; i < 6; ++i)
                {
                    sprintf(printbuf + i * 3, "%02x:", last_buf.mac[i]);
                }
                sprintf(printbuf + 17, " %d %d", last_buf.rssi,last_buf.channel);
                printf("%s\n",printbuf);                
            }
            else
            {
                for(i = 0; i < read_buf_index; ++i)
                {
                    if(memcmp(last_buf.mac ,(pread_buf + i)->mac ,6) == 0 )
                    {
                        if(last_buf.rssi > (pread_buf + i)->rssi)
                        {
                            (pread_buf + i)->rssi = last_buf.rssi;
                        }
                        break;
                    }
                }
                if( i == read_buf_index)
                {
                    memcpy((pread_buf + i)->mac,last_buf.mac,6);
                    (pread_buf + i)->rssi = last_buf.rssi;
                    ++read_buf_index;
                    if(read_buf_index >= READ_BUF_MAX)
                    {
                        read_buf_index = READ_BUF_MAX - 1;
                    }
                }
            }
            last_buf.channel = 0;
        }
        
        if(new_channel == 1 && read_buf_index > 0 && read_cb != NULL )
        //if(new_channel == 2 && read_buf_index > 0 && read_cb != NULL )
        {
            sniffer_stop();
            xTaskCreate(&sniffer_read_task, "sniffer_read_task", 2048, NULL, 10, NULL);
        }
        xSemaphoreGive(semaphore_resource);
    }

}

static void sniffer_read_task(void* pvParameters)
{
    if(xSemaphoreTake(semaphore_resource,portMAX_DELAY) == pdTRUE)
    {
        if(read_cb != NULL)
        {
            (*read_cb)(pread_buf,read_buf_index);
        }
        read_buf_index = 0;
        xSemaphoreGive(semaphore_resource);
    }
    sniffer_start();
    vTaskDelete(NULL);
}